import {
  ArgumentsHost,
  Catch,
  ExceptionFilter,
  HttpException,
  HttpStatus,
  Logger,
} from '@nestjs/common';
import { HttpAdapterHost } from '@nestjs/core';
import { Request, Response } from 'express';
import * as requestIp from 'request-ip';
import { MyHttpException } from '../exceptions/my-http.exception';
import { FailResult } from '@/utils/res';
import { isDev, isProd } from '../config/env';
@Catch()
export class AllExceptionFilter implements ExceptionFilter {
  private readonly logger = new Logger('AllExceptionFilter');

  constructor(private readonly httpAdapterHost: HttpAdapterHost) {}

  catch(exception: any, host: ArgumentsHost) {
    const ctx = host.switchToHttp();
    const request = ctx.getRequest<Request>();
    const response = ctx.getResponse<Response>();
    const httpStatus =
      exception instanceof HttpException
        ? exception.getStatus()
        : HttpStatus.INTERNAL_SERVER_ERROR;
    let message: string | string[] = 'Internal Server Error';
    if (exception instanceof HttpException) {
      const response = exception.getResponse();
      if (typeof response === 'object' && response !== null) {
        message = response['message'] || exception.message;
      } else {
        message = response || exception.message;
      }
    } else if (exception instanceof Error) {
      message = exception.message;
    }
    if (isProd && !(exception instanceof HttpException)) {
      message = 'Internal Server Error';
    }
    let responseBody: any = {
      headers: request.headers,
      body: request.body,
      query: request.query,
      params: request.params,
      message,
      timestamp: new Date().toISOString(),
      httpStatus,
      ip: requestIp.getClientIp(request),
    };
    const stack = exception.stack;
    if (!isDev) {
      this.logger.error(JSON.stringify(responseBody), stack);
    } else {
      this.logger.error(
        `${exception.name}: ${Array.isArray(message) ? message.join(', ') : message}`,
        stack,
      );
    }
    if (exception instanceof MyHttpException) {
      const res = exception.getResponse() as { message: string; code: number };
      responseBody = new FailResult(res.message, res.code);
    }
    this.httpAdapterHost.httpAdapter.reply(response, responseBody, httpStatus);
  }
}
